package org.moonlight.push.server.oppo;

import com.oppo.push.server.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.moonlight.push.server.BaseMessage;

import java.util.*;

/**
 * oppo 手机消息推送 - client
 *
 *
 * 接入文档: https://open.oppomobile.com/new/developmentDoc/info?id=11233
 * 推送运营平台: https://open.oppomobile.com/opush/top/application-list
 *
 * @author Moonlight
 * @date 2022-10-20 15:06
 */
@Slf4j
public class OppoClient {
    /** 目前来说，实际用到的只有 APP_KEY、APP_MASTER_SECRET **/
    private final static String APP_ID = "";
    private final static String APP_KEY = "";
    private final static String APP_SECRET = "";
    private final static String APP_MASTER_SECRET = "";

    /** 标记位 - 标识是否 OppoClient 是否初始化 - false 表示尚未初始化 **/
    private boolean init = false;
    /**
     * Sender 对象 - OPPO SDK提供Sender类封装了OPPO Push推送服务的各类服务接口。因此，使用SDK推送消息或调用其他服务，首先要创建Sender对象。
     * **/
    private Sender sender;

    private final static OppoClient INSTANCE = new OppoClient();

    private OppoClient() {
        if (!init()) {
            throw new RuntimeException("OppoClient初始化失败");
        }
    }

    public static OppoClient getInstance() {
        return INSTANCE;
    }

    private boolean init() {
        if (init) {
            return true;
        }
        if (StringUtils.isBlank(APP_ID)) {
            log.error("[OppoClient init] OppoClient初始化失败. oppo app id 不应该为空");
            return false;
        }
        if (StringUtils.isBlank(APP_KEY)) {
            log.error("[OppoClient init] OppoClient初始化失败. oppo app key 不应该为空");
            return false;
        }
        if (StringUtils.isBlank(APP_SECRET)) {
            log.error("[OppoClient init] OppoClient初始化失败. oppo app key 不应该为空");
            return false;
        }
        if (StringUtils.isBlank(APP_MASTER_SECRET)) {
            log.error("[OppoClient init] OppoClient初始化失败. oppo master secret 不应该为空");
            return false;
        }
        try {
            sender = Sender.newBuilder()
                    .appKey(APP_KEY)
                    .masterSecret(APP_MASTER_SECRET)
                    // 设置当前环境为: 中国
                    .env(Environment.CHINA_PRODUCTION)
                    // 设置http最大连接数
                    .httpMaxConnection(16)
                    // 设置最大http路由连接数
                    .httpMaxRoute(8)
                    // http连接超时时间
                    .httpConnectionTimeout(5000)
                    // 等待连接超时时间
                    .httpConnectRequestTimeout(5000)
                    // socket超时时间
                    .httpSocketTimeout(5000)
                    .build();
        } catch (Exception e) {
            log.error("[OppoClient init] OppoClient初始化失败. 根据appId[{}]appKey[{}]appSecret[{}]masterSecret[{}]构建 OPPO SDK Sender 异常. "
                    , APP_ID, APP_KEY, APP_SECRET, APP_MASTER_SECRET, e);
            return false;
        }

        this.init = true;
        return true;
    }

    /**
     * 功能描述:
     *   推送单推通知栏消息
     * 主要逻辑:
     * 注意事项:
     * @param msg 基础消息实体
     * @return Result oppo 推送服务调用结果 {@link Result}
     * @author moonlight
     * @date 2022/10/20 17:23
     **/
    public Result sendToSingleUser(BaseMessage msg) throws Exception {
        // 调用单推接口，推送消息，获得调用结果
        Notification notification = buildOppoNotificationMsg(msg);

        log.info("[OppoClient sendToSingleUser] 准备推送消息 msg[{}] notification[{}]", msg, notification);

        Result result = sender.unicastNotification(notification, Target.build(msg.getReceiverId()));

        log.info("[OppoClient sendToSingleUser] 向[{}]推送消息[{}]完成. 原始消息[{}] 推送结果[{}]", msg.getReceiverId(), notification, msg, result);

        return result;
    }

    /**
     * 功能描述:
     *   推送批量单推通知栏消息
     * 主要逻辑:
     * 注意事项:
     * @param msg 基础消息实体
     * @param targetIds 用户信息
     * @return Result oppo 推送服务调用结果 {@link Result}
     * @author moonlight
     * @date 2022/10/20 17:26
     **/
    public Result sendToMultiUser(BaseMessage msg, List<String> targetIds) throws Exception {
        if (targetIds == null || targetIds.size() == 0) {
            log.error("[OppoClient sendToMultiUser] targetIds不能为空");
            throw new RuntimeException("参数错误, targetIds不能为空");
        }
        if (targetIds.size() > 1000) {
            log.error("[OppoClient sendToMultiUser] targetIds不能超过1000. oppo sdk 要求 batch最大长度为1000，超出会导致请求失败");
            throw new RuntimeException("参数错误, targetIds不能超过1000");
        }
        log.info("[OppoClient sendToMultiUser] 准备向[{}]位用户推送msg[{}]. 第一位用户[{}]", targetIds.size(), msg, targetIds.get(0));

        // batch最大长度为1000，超出会导致请求失败
        Map<Target, Notification> batch = new HashMap<>(16);
        // 创建目标对象并加入到批量队列中
        Notification notification = buildOppoNotificationMsg(msg);
        for (String id : targetIds) {
            batch.put(Target.build(id), notification);
        }
        // 调用批量单推接口，推送消息，获得调用结果
        Result result = sender.unicastBatchNotification(batch);

        log.info("[OppoClient sendToMultiUser] 推送消息完成 notification[{}] msg[{}] result.statusCode[{}] result.reason[{}] result.returnCode[{}]"
                , notification, msg, result.getStatusCode(), result.getReason(), result.getReturnCode());

        return result;
    }

    /**
     * 功能描述:
     *   推送广播消息分为两步
     *   首先调用保存消息体接口，保存广播消息内容，得到消息ID
     *   使用消息ID调用广播推送接口，推送广播消息
     * 主要逻辑:
     * 注意事项:
     * @param msg 基础消息实体
     * @return Result  oppo 推送服务调用结果 {@link Result}
     * @author moonlight
     * @date 2022/10/21 14:34
     **/
    public Result sendBroadMsg(BaseMessage msg) throws Exception {
        // 创建通知栏消息体
        Notification broadNotification = buildOppoNotificationMsg(msg);
        // 发送保存消息体请求
        Result saveResult = null;
        try {
            saveResult = sender.saveNotification(broadNotification);
        } catch (Exception e) {
            log.error("[OppoClient sendBroadMsg] 发送保存消息体请求时出错. broadNotification[{}] msg[{}]. ", broadNotification, msg, e);
            throw e;
        }
        log.info("[OppoClient sendBroadMsg] 发送保存消息体请求成功 saveResult[{}] broadNotification[{}] msg[{}]", saveResult, broadNotification, msg);
        // 创建推送目标 广播目标接受多个注册ID，字符串中以英文分号';'分割
        Target target = new Target();
        target.setTargetValue(msg.getReceiverId());
        // 发送广播消息
        return sender.broadcastNotification(saveResult.getMessageId(), target);
    }

    /**
     * 功能描述:
     *   根据 传入的 BaseMessage 对象生成 OPPO SDK的 Notification 消息体
     * 主要逻辑:
     * 注意事项:
     * @param msg 基础消息实体
     * @return Notification OPPO SDK推送消息体 {@link Notification}
     * @author moonlight
     * @date 2022/10/20 16:48
     **/
    private Notification buildOppoNotificationMsg(BaseMessage msg) {
        Notification notification = new Notification();
        // 标题，内容是必填项

        notification.setTitle(msg.getAlertTitle());
        notification.setContent(msg.getAlertContent());
        /*
         * 以下参数非必填项，参考OPPO push服务端api文档进行设置，本示例选取部分参数进行示范
         */
        // 通知栏样式 1. 标准样式  2. 长文本样式  3. 大图样式 【非必填，默认1-标准样式】
        notification.setStyle(1);
        // App开发者自定义消息Id，OPPO推送平台根据此ID做去重处理，对于广播推送相同appMessageId只会保存一次，对于单推相同appMessageId只会推送一次
        notification.setAppMessageId(msg.getMsgId());

        // 应用接收消息到达回执的回调URL，字数限制200以内，中英文均以一个计算
//        notification.setCallBackUrl("http://www.test.com");
        // App开发者自定义回执参数，字数限制50以内，中英文均以一个计算
//        notification.setCallBackParameter("");

        // 点击动作类型0，启动应用；1，打开应用内页（activity的intent action）；2，打开网页；4，打开应用内页（activity）；【非必填，默认值为0】;5,Intent scheme URL
        // 默认行为 - 启动应用
        notification.setClickActionType(4);

        // 应用内页地址【click_action_type为1或4时必填，长度500】
        notification.setClickActionActivity("安卓Activity全类名");
        // 动作参数，打开应用内页或网页时传递给应用或网页【JSON格式，非必填】，字符数不能超过4K，示例：{"key1":"value1","key2":"value2"}
        notification.setActionParameters("{\"launchFlags\" : \"0x4000000\", \"caseId\" : \"4127\", \"intoType\" : \"1\"}");
        // 网页地址【click_action_type为2必填，长度500】
//        notification.setClickActionUrl("http://www.test.com");

        // 展示类型 (0, “即时”),(1, “定时”)
        notification.setShowTimeType(0);

        // 定时展示开始时间（根据time_zone转换成当地时间），时间的毫秒数
//        notification.setShowStartTime(System.currentTimeMillis() + 1000 * 60 * 3);
        // 定时展示结束时间（根据time_zone转换成当地时间），时间的毫秒数
//        notification.setShowEndTime(System.currentTimeMillis() + 1000 * 60 * 5);

        // 是否进离线消息,【非必填，默认为True】
        notification.setOffLine(true);
        // 离线消息的存活时间(time_to_live) (单位：秒), 【off_line值为true时，必填，最长3天】
        notification.setOffLineTtl(24 * 3600);
        // 时区，默认值：（GMT+08:00）北京，香港，新加坡
        notification.setTimeZone("GMT+08:00");
        // 0：不限联网方式, 1：仅wifi推送
        notification.setNetworkType(0);

        return notification;
    }

    public static void demo() throws Exception {
        Sender sender = Sender.newBuilder()
                .appKey(APP_KEY)
                .masterSecret(APP_MASTER_SECRET)
                // 设置当前环境为: 中国
                .env(Environment.CHINA_PRODUCTION)
                // 设置http最大连接数
                .httpMaxConnection(16)
                // 设置最大http路由连接数
                .httpMaxRoute(8)
                // http连接超时时间
                .httpConnectionTimeout(5000)
                // 等待连接超时时间
                .httpConnectRequestTimeout(5000)
                // socket超时时间
                .httpSocketTimeout(5000)
                .build();

        // 创建通知栏消息体
        Notification notification = getNotification();
        // 创建推送目标对象，传入注册ID
        Target target = Target.build("CN_8fa0618f178145d8c2a44091a1326411");
        // 调用单推接口，推送消息，获得调用结果
        Result result = sender.unicastNotification(notification, target);

        System.out.println("demo result: " + result);
    }

    private static Notification getNotification() {
        Notification notification = new Notification();

        // 标题，内容是必填项
        notification.setTitle("通知栏消息tile");
        notification.setContent("通知栏内容");
        /*
         * 以下参数非必填项，参考OPPO push服务端api文档进行设置，本示例选取部分参数进行示范
         */
        //通知栏样式 1. 标准样式  2. 长文本样式  3. 大图样式 【非必填，默认1-标准样式】
        notification.setStyle(1);
        // App开发者自定义消息Id，OPPO推送平台根据此ID做去重处理，对于广播推送相同appMessageId只会保存一次，对于单推相同appMessageId只会推送一次
        notification.setAppMessageId(UUID.randomUUID().toString());
        // 应用接收消息到达回执的回调URL，字数限制200以内，中英文均以一个计算
        notification.setCallBackUrl("http://www.test.com");
        // App开发者自定义回执参数，字数限制50以内，中英文均以一个计算
        notification.setCallBackParameter("");
        // 点击动作类型0，启动应用；1，打开应用内页（activity的intent action）；2，打开网页；4，打开应用内页（activity）；【非必填，默认值为0】;5,Intent scheme URL
        notification.setClickActionType(4);
        // 应用内页地址【click_action_type为1或4时必填，长度500】
        notification.setClickActionActivity("com.coloros.push.demo.component.InternalActivity");
        // 网页地址【click_action_type为2必填，长度500】
        notification.setClickActionUrl("http://www.test.com");
        // 动作参数，打开应用内页或网页时传递给应用或网页【JSON格式，非必填】，字符数不能超过4K，示例：{"key1":"value1","key2":"value2"}
        notification.setActionParameters("{\"key1\":\"value1\",\"key2\":\"value2\"}");
        // 展示类型 (0, “即时”),(1, “定时”)
        notification.setShowTimeType(1);
        // 定时展示开始时间（根据time_zone转换成当地时间），时间的毫秒数
        notification.setShowStartTime(System.currentTimeMillis() + 1000 * 60 * 3);
        // 定时展示结束时间（根据time_zone转换成当地时间），时间的毫秒数
        notification.setShowEndTime(System.currentTimeMillis() + 1000 * 60 * 5);
        // 是否进离线消息,【非必填，默认为True】
        notification.setOffLine(true);
        // 离线消息的存活时间(time_to_live) (单位：秒), 【off_line值为true时，必填，最长3天】
        notification.setOffLineTtl(24 * 3600);
        // 时区，默认值：（GMT+08:00）北京，香港，新加坡
        notification.setTimeZone("GMT+08:00");
        // 0：不限联网方式, 1：仅wifi推送
        notification.setNetworkType(0);
        return notification;
    }

    public static void main(String[] args) throws Exception {
//        demo();

        BaseMessage msg = new BaseMessage();
        msg.setReceiverId("OPPO_CN_b7a7b28c49dbe02318bdb128d6f8b330");
        msg.setAlertTitle("来自Moonlight的Project");
        msg.setAlertContent("PushServerProjectPushOPPO消息" + System.currentTimeMillis());

        Result result = OppoClient.getInstance().sendToSingleUser(msg);
        System.out.println("\r\n sendToSingleUser result = " + result);
    }

}